<?php
/**
 * 
 * +------------------------------------------------------------+
 * @category Model 
 * +------------------------------------------------------------+
 * 数据层操作模型类
 * +------------------------------------------------------------+
 *
 * @author anzai <236049524@qq.com>
 * @copyright http://www.judianyun.com 2012
 * @version 1.0
 *
 * Modified at : 2012-02-17 11:29:11
 *
 */
class Model extends Base {
    
    const FORCE_CACHE_IDENTY = 'YES';
    
	/**
	 * 
	 * Cache对象
	 * @var object
	 */
	protected $_cache = null;
	
	/**
	 * 
	 * 数据库驱动类实例
	 * @var object
	 */
	protected $_db = null;
	
	/**
	 * 默认表前缀，若该属性有设置则使用该属性为默认表前缀
	 * @var string
	 */
	protected $_tablepre = null;
	
	/**
	 * 
	 * 数据库相关配置
	 * @var array
	 * 
	 *  选项包括:
	 * 	dbtype => 数据类型，当前支持mysql和mssql，默认为mysql
	 * 	dbhost => 数据库主机访问地址，默认为127.0.0.1
	 * 	dbport => 端口号，默认3306
	 * 	dbuser => 用户名，默认root
	 * 	dbpass => 密码，默认为空
	 * 	dbname => 所访问的数据库名
	 *  tablepre => 表前缀
	 *  charset => 字符编码
	 *  pconnect => 是否启用永久连接标识，默认false
	 *  caching => 是否开启数据缓存功能，默认false
	 */
	protected $_dbConfig = null;
	
	/**
	 * 数据库连接数
	 */
	protected $_db_connect_index = 0;
	
	/**
	 * 
	 * 提交数据时出现的错误信息
	 * @var array
	 */
	protected $_errors = null;

	/**
	 * 
	 * 当前加载数据的的ID
	 * @var int
	 */
	protected $_lineId = null;

	/**
	 * 
	 * ID字段对应的字段名称，默认为id
	 * @var string
	 */
	protected $_idField = 'id';
	
	/**
	 * 
	 * 最后一次插入数据库中的ID
	 * @var int
	 */
	protected $_lastId;
	
	/**
	 * 
	 * 存储当前所操作的表的所有字段信息
	 * @var array
	 */
	protected $_properties = array();
	
	/**
	 * 
	 * Select对象
	 * @var object
	 */
	protected $_select = null;
	
	/**
	 * 
	 * 是否在处于查询状态标识
	 * @var boolean
	 */
	private $_status = true;
	
	/**
	 * 
	 * 当前操作的表名
	 * @var string
	 */
	protected $_table = null;
	
	/**
	 * -------------------------------------------------------------------------
	 * 表单提交操作所要进行验证数据合法性的字段，一个二维数组
	 * 用法如下：
	 * array(
	 * 		'字段标识'=>array(
	 * 			'regex'=> '格式验证',默认为require任意格式
	 * 			'empty' => 是否可以为空,默认为false不能为空
	 * 			'unique'=> 值是否唯一,默认为false，true标识唯一值
	 * 			'action'=> '添加数据或更新数据或两者才进行验证',add：添加，edit：更新数据，其他表示两者都进行验证
	 * 			'desc'=> '字段描述',
	 * 			'func'=> '预处理函数',   		  //预处理函数处理后返回处理后数据
	 * 			'verify' => '验证数据正确性函数',  //如匹配验证码的正确性，正确则返回true，否则返回false
	 * 			'min'=> 长度最小值，0或空表示不限制
	 * 			'max'=> 长度最大值，0或空表示不限制
	 * 			'eq'	=> '' 验证与其他字段是否相等，默认为空(如有密码等于确认密码，则填写为repwd)
	 * 			'gt'	=> 值大于（可为某个值或对应某个表单域，下同）
	 * 			'lt'	=> 值小于
	 * 			'egt'	=> 值大于等于
	 * 			'elt'	=> 值小于等于
	 * 		)
	 * )
	 * -------------------------------------------------------------------------
	 * @var $_rules
	 */
	protected $_rules = array();
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name __construct
	 * +------------------------------------------------------------+
	 * 构造函数
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param mixed $id 预加载数据行的ID
	 * @param string $table 所操作的表名，默认同模型类名一致
	 * @param string $idField ID字段对应的字段名，默认为id
	 * @param array $dbConfig 自定义数据库配置
	 *
	 */
	function __construct($id=null, $table='', $idField='', $dbConfig=null){
		parent::__construct();
		if(!empty($idField)){
			$this->_idField = $idField;
		}
		//初始化
		$this->_init();
		
		//检查用户是否有自动传入自定义数据库配置
		if ($dbConfig!==null && $dbConfig){
			$this->_dbConfig = $dbConfig;
			
			//若使用自定义数据库配置，则重新创建一个数据库连接实例
			$this->_db_connect_index++;
		}
			
		
		$this->_initialize();
		$class = get_class($this);
		
		if ($class != __CLASS__ && empty($table) && empty($this->_table)){
			$table = substr($class, 0, -strlen(MODEL_SUFFIX));
			$tablepre = empty($this->_tablepre) ? $this->_dbConfig['tablepre'] : $this->_tablepre;
			$this->_table = empty($table) ? '' : $tablepre . $table;
			!$this->_properties && $this->setField();
		}elseif ($table){
			$this->setTable($table);
		}
		
		if($id!==null){
			$this->load($id);
		}
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name count
	 * +------------------------------------------------------------+
	 * 统计数据条数
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param string $sql 查询SQL语句，可为空。
	 * @param boolean $cache
	 *
	 */
	function count($sql = '', $cache = false){
	    if ($sql == ''){
			$this->getSelect()->column('count(1)');
			$sql = $this->_getSql();
		}
		
		$count = $this->_getCacheData($sql, $cache);
		if ($count === null) {
			$count = $this->getDb('count')->count($sql);
			if ($cache){
			    $this->_setCacheData($sql, $count, $cache === self::FORCE_CACHE_IDENTY);
			}
		}
		return $count;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name dataExists
	 * +------------------------------------------------------------+
	 * 判断字段名为$name值为$value在当前操作的表中是否存在
	 * +------------------------------------------------------------+
	 *
	 * @param string $name
	 * @param string $value
	 *
	 */
	function dataExists($name, $value){
		$this->getSelect()->reset()->where('`'.$name.'`=?', $value);
		if($this->isSaved()) $this->getSelect()->where($this->_idField.'<>?', $this->getId());
		return $this->count()>0;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name delete
	 * +------------------------------------------------------------+
	 * 删除指定ID的数据或批量删除数据或清空数据库表
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 * 		1、delete(1)表示在当前加载表内删除ID为1的数据行
	 * 		2、delete()清空当前加载单位数据库表，delete(null,null,'table')清空指定的表table的所有数据
	 * 		3、delete('id=?',1,'table')从表table中删除id=1的数据行，delete('id=1','','table')同理
	 * 		4、delete('id=? and name=?',array(1,'test')从当前加载的表中删除id为1，name为test的所有相关数据
	 *
	 * +------------------------------------------------------------+
	 * @param mixed $where 可为一个数字或字符串，数字表示指定要删除的数据行ID，为字符串中带有占位符“?”（可含多个）；
	 * 为null时，若有加载数据指定数据行ID，则删除当前指定ID的数据行，否则清空表
	 * @param mixed $replace 替换占位符“?”的值，字符串或数组，数组时进行逐个替换
	 * @param string $table 所操作的目标数据库表，默认当前加载的表
	 * @param boolean $transaction 是否进行事务处理
	 * @return 成功返回true
	 * +------------------------------------------------------------+
	 *
	 */
	function delete($where = null, $replace = null, $table = null, $transaction = true){
		//进行批量删除数据
		if ($where && is_array($where)){
			foreach ($where as $w){
				if (!$this->delete($w, false)){
				    $this->getDb()->rollback();
				    return false;
				}
			}
			
			if ($this->postSubmit()){
			    if ($transaction){
			        if ($this->getDb()->commit()){
			            $this->postSaved();
			            return true;
			        }else{
			            $this->getDb()->rollback();
			            return false;
			        }
			    }else{
			        $this->postSaved();
			        return true;
			    }
			}else{
			    $this->getDb()->rollback();
			    return false;
			}
		}
		
		if (is_numeric($where) && intval($where) > 0){
			$this->_lineId = intval($where);
		}
		
		$this->getDb()->begin();
		
		if(!$this->preDelete()){
			if($transaction) $this->getDb()->rollback();
			return false;
		}
		
		$where = empty($where) ? null : $where;
		if(!$this->D($where, $replace, $table)){
			if($transaction) $this->getDb()->rollback();
			return false;
		}
		
		if(!$this->postDelete()){
			if($transaction) $this->getDb()->rollback();
			return;
		}
		$this->_lineId = null;
		
		if ($replace !== false){
		    if ($transaction){
		        if ($this->getDb()->commit()){
		            $this->postSaved();
		            return true;
		        }else{
		            $this->getDb()->rollback();
		            return false;
		        }
		    }else{
		        $this->postSaved();
		        return true;
		    }
		}else{
		    return true;
		}
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name D
	 * +------------------------------------------------------------+
	 * 删除指定ID的数据或批量删除数据或清空数据库表
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 * 		1、D(1)表示在当前加载表内删除ID为1的数据行
	 *  	2、D()清空当前加载单行数据库表，D(null,null,'table')清空指定的表table的所有数据
	 *  	3、D('id=?',1,'table')从表table中删除id=1的数据行，D('id=1','','table')同理
	 *  	4、D('id=? and name=?',array(1,'test')从当前加载的表中删除id为1，name为test的所有相关数据
	 *
	 * +------------------------------------------------------------+
	 * @param mixed $where 可为一个数字或字符串，数字表示指定要删除的数据行ID，为字符串中带有占位符“?”（可含多个）；
	 * 为null时，若有加载数据指定数据行ID，则删除当前指定ID的数据行，否则清空表
	 * @param mixed $replace 替换占位符“?”的值，字符串或数组，数组时进行逐个替换
	 * @param string $table 所操作的目标数据库表，默认当前加载的表
	 * @return 成功返回true
	 * +------------------------------------------------------------+
	 *
	 */
	public function D($where=null, $replace=null, $table=null){
		//进行批量删除数据
		if (!empty($where) && is_array($where)){
			foreach ($where as $w){
				if (!$this->D($w)) return false;
			}
			
			return true;
		}
		if ($where !== false && ((!$where && $this->isSaved()) || is_numeric($where))){
			$replace = is_numeric($where) ? (int)$where : $this->getId();
			$where = $this->_idField.'=?';
			if ($replace){
				$this->getSelect()->where($replace);
			}
		}
		
		if ($table){
			$this->getSelect()->from($table);
		}
		
		$this->getSelect()->where($where, $replace);
		
		$sql = $this->_getSql(1);
		
		if (stripos($sql, 'where') === false){
			$this->_errors[] = '为了数据安全不允许清空表数据，请设置删除数据条件';
			return false;
		}
		
		return $this->getDb()->exec($sql);
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name getAll
	 * +------------------------------------------------------------+
	 * 获取查询所有结果集
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param string $sql 若查询语句$sql为空时，自动获取用户根据条件自动组装的SQL语句；若该SQL语句也为空，则默认获取当前表的所有数据
	 * @param boolean $cache 是否将查询结果存储到缓存（只有开启了缓存功能，该值才有效），默认为true
	 *
	 */
	public function getAll($sql='', $cache = true){
		$sql = empty($sql) ? $this->_getSql() : $sql;
		
		$data = $this->_getCacheData($sql, $cache);
		if (empty($data)) {
			$data = $this->getDb()->getAll($sql);
			
			if ($cache){
			    $this->_setCacheData($sql, $data, $cache === self::FORCE_CACHE_IDENTY);
			}
		}
		
		return $data;
	}
	
	/**
	 * 方法getAll的别名
	 */
	public function select($sql='', $cache = true){
		return $this->getAll($sql, $cache);
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name _getCacheData
	 * +------------------------------------------------------------+
	 * 获取缓存数据
	 * +------------------------------------------------------------+
	 *
	 * @param string $key
	 * @param mixed  $cache
	 *
	 */
	private function _getCacheData($key, $cache = true){
	    $obj = $this->_cache;
	    if (!$obj && $cache === self::FORCE_CACHE_IDENTY){
	        $obj = $this->_getCache(true);
	    }
	    
	    if(!$cache || !$obj){
		    return;
		}
		
		return $obj->get($key);
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name getConfig
	 * +------------------------------------------------------------+
	 * 获取数据库相关配置，可具体指定哪个配置
	 * +------------------------------------------------------------+
	 *
	 * @param string $name
	 *
	 */
	public function getConfig($name = null){
		if ($name==null) 
			return $this->_dbConfig;
		elseif(isset($this->_dbConfig[$name]))
			return $this->_dbConfig[$name];
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name getDb
	 * +------------------------------------------------------------+
	 * 获取数据库驱动类实例
	 * +------------------------------------------------------------+
	 *
	 * @example
	 *
	 *
	 */
	public function getDb($method = ''){
		if ($this->_db === null){//若数据库驱动类还未实例化，则进行实例化
			$db = Registry::get('db' . $this->_db_connect_index);  //从注册表中获取数据库驱动类对象
			if ($db)//若已连接数据库则不在进行连接
				$this->_db = $db;
			else{
				$classname = get_class($this);
				$rf = new ReflectionClass($classname);
				$this->_db =  Factory::db($this->_dbConfig, array(
					'from'	=> $classname . ($method == '' ? '' : '::' . $method),
					'file'  => $rf->getFileName()
				));
				unset($rf);
				Registry::set('db', $this->_db);  //数据库驱动类对象写入注册表
			}
			if($this->_table)
				$this->_db->setTable($this->_table);
		}
		
		return $this->_db;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name getError
	 * +------------------------------------------------------------+
	 * 获取表单验证过程中产生的错误信息，可指定要获取的错误信息，未指定则返回所有错误信息
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example 获取所有：getError()   获取指定：getError('username')
	 *
	 * @param string $key 指定获取的错误信息
	 *
	 */
	public function getError($key=''){
		if ($key == ''){
			if ($this->_errors){
				$errors = array_values($this->_errors);
				return $errors[0];
			}
		}
		elseif (isset($this->_errors[$key])){
			return $this->_errors[$key];
		}
	}
	
	/**
	 * 获取当前已经加载的数据行
	 */
	public function getLoadData(){
		if ($this->isSaved()){
			$data = array();
			foreach ($this->_properties as $k => $f){
				$data[$k] = $f['value'];
			}
			
			return $data;
		}else{
			return null;
		}
	}
	
	/**
	 * 获取指定字段已设置的值
	 */
	public function getFieldValue($field){
		return isset($this->_properties[$field]) ? $this->_properties[$field]['value'] : null;
	}
	
	/**
	 * 获取当前表的所有字段
	 */
	public function getFields($keyValue = false){
		empty($this->_properties) && $this->setField();
		if (!$keyValue){
		    return array_keys($this->_properties);
		}else{
		    $result = array();
		    foreach ($this->_properties as $k => $vo){
		        $result[$k] = $vo['value'];
		    }
		    
		    return $result;
		}
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name hasField
	 * +------------------------------------------------------------+
	 * 判断是否存在某个字段
	 * +------------------------------------------------------------+
	 *
	 * @param string $field
	 *
	 */
	public function hasField($field){
		empty($this->_properties) && $this->setField();
		return array_key_exists($field, $this->_properties);
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name getId
	 * +------------------------------------------------------------+
	 * 获取当前所加载数据行的ID
	 * +------------------------------------------------------------+
	 *
	 * @example
	 *
	 *
	 */
	public function getId(){
		return $this->_lineId;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name getLastId
	 * +------------------------------------------------------------+
	 * 取得上一步 INSERT 操作产生的 ID 
	 * +------------------------------------------------------------+
	 *
	 * @example
	 *
	 *
	 */
	public function getLastId(){
		return $this->_lastId>0 ? $this->_lastId : $this->getDb()->lastInsertId();
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name getOne
	 * +------------------------------------------------------------+
	 * 从数据表中获取一条数据
	 * +------------------------------------------------------------+
	 *
	 * @param string $sql 若查询语句$sql为空时，自动获取用户根据条件自动组装的SQL语句；
	 * 若该SQL语句也为空，则默认获取当前表的最新的一条数据；若大于0则自动加载ID为该值的数据行
	 * 
	 * @param boolean $cache 是否将查询结果存储到缓存（只有开启了缓存功能，该值才有效），默认为true
	 * @return
	 *
	 */
	public function getOne($sql = '', $cache = true){
		if (empty($sql)){
			if($this->_status)
				$this->getSelect()->limit(1, 0);
				
			$sql = $this->_getSql();
		}elseif (is_numeric($sql) && $sql > 0){
			$sql = $this->where($this->_idField . '=?', $sql)->limit(1)->_getSql();
		}
		$data = $this->_getCacheData($sql, $cache);
		if (empty($data)){
			$data = $this->getDb('getOne')->getOne($sql);
			if ($cache)
			    $this->_setCacheData($sql, $data, $cache === self::FORCE_CACHE_IDENTY);
		}
		return $data;
	}
	
	/**
	 * 方法getOne的别名
	 */
	public function find($sql='', $cache=true){
		return $this->getOne($sql, $cache);
	}
	
	/**
	 * 获取最后一次执行的SQL语句
	 */
	public function getLastSql(){
		$count = isset($GLOBALS['DB_QUERYS']) && is_array($GLOBALS['DB_QUERYS']) ? count($GLOBALS['DB_QUERYS']) : 0;
		$sql = '';
		if ($count > 0){
			$sql = $GLOBALS['DB_QUERYS'][$count-1]['sql'];
		}
		
		return $sql;
	}
	
	/**
	 * 
	 * 获取用户自定义查询语句
	 */
	private function _getSql($type = 0){
		//若所要操作的表还未设置，则默认设置为当前表
		if($this->getSelect()->isEmpty(Select::FROM)){
			$this->getSelect()->from($this->_table);
		}
		switch ($type){
			case 1://获取删除数据的语句
				$sql = $this->getSelect()->delete();
				break;
				
			case 2: //获取update语句
				$sql = $this->getSelect()->update();
				break;
				
			default:
				$sql = $this->getSelect()->sql();
		}
		
		$this->getSelect()->reset();
		$this->_status = true;
		return $sql;
	}
	
	/**
	 * 获取当前正在操作的表名
	 */
	public function getTable(){
		return $this->_table;
	}
	
	/**
	 * 类初始化，检查数据库配置是否已设置，若已设置这实例化相关应的数据库驱动类
	 */
	private function _initialize(){
		if($this->_dbConfig === null){
			$this->_dbConfig = C('DB_CONFIG');
		}
		
		if ($this->_select === null){
			require_once __DB_PATH__.'Select.class.php';
			$this->_select = new Select();
			//$this->_select->setModel($this);
		}
		
		$this->_getCache();
	}
	
	protected function _getCache($forceOpen = false){
	    if ($this->_cache === null && ($forceOpen === true || (isset($this->_dbConfig['caching']) && $this->_dbConfig['caching']))){
	        $cacheSetting = C('CACHE_CONFIG');
	        if ($cacheSetting){
	            $this->_cache = Factory::cache($cacheSetting);
	        }
	    }
	    
	    return $this->_cache;
	}
	
	/**
	 * 启动数据事务回滚共
	 */
	public function begin(){
		return $this->getDb()->begin();
	}
	
	/**
	 * 执行过程有错误，开始数据回滚
	 */
	public function rollback(){
		return $this->getDb()->rollback();
	}
	
	/**
	 * 执行过程中无错误，保存数据
	 */
	public function commit(){
		return $this->getDb()->commit();
	}
	
	/**
	 * 插入数据，参数表示否启用事务回滚功能
	 */
	protected function _insert($transaction = true){
		if($transaction) $this->getDb()->begin();
		if (!$this->preInsert()){
			if($transaction) $this->getDb()->rollback();
			return false;
		}
		
		if (!empty($this->_properties)){
			foreach ($this->_properties as $field => $v){
				if ($v['updated']){
					$fields[] = $field;
					$values[] = is_array($v['value']) ? serialize($v['value']) : $v['value'];
				}
			}
			if (!empty($fields)){
				if (!$this->insert($fields, $values)){
					if($transaction) $this->getDb()->rollback();
					return false;
				}
			}
		}
		if (!$this->postInsert() || !$this->postSubmit()) {
			if($transaction) $this->getDb()->rollback();
			return false;
		}
		
		if($transaction){
		    if ($this->getDb()->commit()){
		        $this->postSaved();
		        return true;
		    }else{
		        $this->getDb()->rollback();
		        return false;
		    }
		}else{
		    $this->postSaved();
		    return true;
		}
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name insert
	 * +------------------------------------------------------------+
	 * 通用插入数据方法，只支持单个表操作
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example 
	 * 示例1：insert(array('field'=>'value'))
	 * 示例2：insert(array('field'=>'value'),'table')
	 * 示例3：insert(array('field1','field2'),array('v1','v2'))
	 * 示例4：insert(array('field1','field2'),array('v1','v2'),'table')
	 * 示例5：insert(array('field1','field2'),array(array('v1','v2'),array('v3','v4')))
	 *
	 * @param Array $fields 字段，若格式为array('col1'=>'val1','colN'=>'valN')形式则$values可为空或做为表名，表示插入一条数据
	 * @param Array $values 值，插入单条数据时使用一维数组；多条时，使用二维数组
	 * @param String $table 指定表，默认当前表
	 *
	 */
	public function insert($fields=null, $values=null, $table=null){
		if (is_array($values) && !empty($values)){
			$repeat = Helper::repeat('?', count($fields), true);
			$keys = array_keys($values);
			if (is_array($values[$keys[0]])){//判断是否为二维数组
				foreach ($values as $k=>$v)
					$values[$k] = $this->getSelect()->quoteInto($repeat, $v);
			}else
				$values = $this->getSelect()->quoteInto($repeat, $values);
			unset($keys);
		}elseif (!empty($values)){
			$table = empty($table) ? $values : $table;
			unset($values);
		}
		$table = empty($table) ? $this->getTable() : $table;
		
		$res = $this->getDb('insert')->insert($this->getSelect()->addQuoMark($fields), $values, $table);
		$this->_lastId = $res ? $this->getLastId() : 0;
		return $res;
	}
	
	/**
	 * 方法insert的别名
	 */
	public function add($fields=null, $values=null, $table=null){
		return $this->insert($fields, $values, $table);
	}
	
	/**
	 * 判断当前是否有加载数据
	 */
	public function isSaved(){
		$id = $this->getId();
		$isSaved = empty($id) || $id === null;
		return !$isSaved;
	}
	
	//判断$name在表字段信息中 是否已设置
	public function isseted($name){
		return array_key_exists($name, $this->_properties);
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name load
	 * +------------------------------------------------------------+
	 * 加载一条数据
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param mixed $id 所要加载的ID，为空则不进行任何操作
	 * @param string $field 加载字段
	 * @param boolean $cache 是否进行缓存
	 *
	 */
	public function load($id = null, $cache=true){
		$field = empty($field) ? '*' : $field;
		$id = $id !== null ? trim($id) : '';

		if(!empty($id)){
			// 若查询字段未设置者默认设置为获取所有字段信息
			if ($this->getSelect()->isEmpty(Select::COLUMNS)){
				$this->column('*');
			}
			$this->getSelect()->where($this->_idField.'=?', $id);
		}else if ($this->getSelect()->isEmpty(Select::WHERE)){
			return false;
		}
		
		$data = $this->getOne($this->_getSql(), $cache);
		if (!empty($data)){
			foreach ($data as $key=>$value){
				$this->_properties[$key]['value'] = !empty($value) && is_array(unserialize($value)) ? unserialize($value) : $value;
			}
			$this->_lineId = $data[$this->_idField];
		}else
			$this->_lineId = null;
		$this->postLoad();
		
		return $data;
	}
	
	/**
	 * 用户自定义初始化操作
	 */
	protected function _init(){
		
	}
	
	/**
	 * 删除数据前所进行的操作，子类重写必须返回一个true布尔值，否则不再继续执行后面相关操作（下同）
	 */
	protected function preDelete(){
		return true;
	}
	
	/**
	 * 删除后回调方法
	 */
	protected function postDelete(){
		return true;
	} 
	
	/**
	 * 插入数据前回调方法
	 */
	protected function preInsert(){
		return true;
	}
	
	/**
	 * 插入数据后调方法
	 */
	protected function postInsert(){
		return true;
	}
	
	/**
	 * 修改前回调方法
	 */
	protected function preUpdate(){
		return true;
	}
	
	/**
	 * 修改后回调方法
	 */
	protected function postUpdate(){
		return true;
	}
	
	/**
	 * 数据加载完后（即执行load()后）回调方法
	 */
	protected function postLoad(){
		
	}
	
	/**
	 * 插入数据之后、更新数据之后、删除数据之后都会调用到的方法
	 */
	protected function postSubmit(){
		return true;
	}
	
	/*
	 * 执行submit前的回调方法
	 */
	protected function preSubmit(){
		return true;
	}
	
	/*
	 * 数据保存成功后执行的回调方法
	 */
	protected function postSaved(){
	    
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name query
	 * +------------------------------------------------------------+
	 * 执行SQL语句
	 * +------------------------------------------------------------+
	 *
	 * @example
	 *
	 * @param string $sql
	 * @param mixed $replace
	 *
	 */
	public function query($sql, $replace=''){
		$sql = $this->getSelect()->quoteInto($sql, $replace);
		return $this->getDb('query')->exec($sql);
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name save
	 * +------------------------------------------------------------+
	 * 执行插入数据或更新数据操作
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param boolean $transaction 是否启用事物回滚
	 *
	 */
	public function save($transaction = true){
		if($this->isSaved()){
			$queryID = $this->_update($transaction);
		}else
			$queryID = $this->_insert($transaction);
		
		return $queryID;
	}
	
	/**
	 * 获取Select对象实例
	 */
	public function getSelect(){
		$this->_status = false;
		try{
			if ($this->_select===null)
				throw new E(Helper::L('CLS_MODEL_SELECT'), 'db');
		}catch (E $e){
			$e->getMsg();
		}
		
		return $this->_select;
	}

	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name set
	 * +------------------------------------------------------------+
	 * 设置指定表字段的值
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param string $name  字段名
	 * @param string $value 值
	 *
	 */
	public function set($name, $value = ''){
		if(isset($this->_properties[$name]) && $value != $this->_properties[$name]['value']){
			$this->_properties[$name]['value'] = $value;
			$this->_properties[$name]['updated'] = true;
		}
		
		return $this;
	}
	
	/**
	 * 插入或更新数据设置值 
	 * @param mixed $field
	 * @param string $value
	 * @return Model
	 */
	public function setvalue($field, $value = ''){
		$this->getSelect()->setvalue($field, $value);
		
		return $this;
	}
	
	/**
	 * 将数值写到缓存中
	 */
	private function _setCacheData($key, $value, $force = ''){
	    $obj = $this->_cache;
	    if (!$obj && $force === self::FORCE_CACHE_IDENTY){
	        $obj = $this->_getCache(true);
	    }
	    
	    if (!$obj || !$value){
		    return;
		}
		
		$obj->set($key, $value);
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name setField
	 * +------------------------------------------------------------+
	 * 手动或自动设置表字段，当未传入参数时，自动从数据可中读取表结构
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 * 		setField("testField1","testValue2")、setField("testField1","testValue2")
	 * 		setField(array("testField1","testField2"),array("testValue1","testValue2"))
	 *
	 * @param mixed $field 字段
	 * @param mixed $default 值
	 *
	 */
	protected function setField($field='', $default=''){
		if($this->_table===null) return;
		if (empty($field)){//字段获取表字段信息
			$saveTableCache = C('TABLE_CACHE', true);
			$properties = array();
			$cachedir = TEMP_PATH . 'Table' . DIRECTORY_SEPARATOR;
			if($saveTableCache){//判断是否开启存储表结构功能
				//若表结构缓存文件已存在，直接从缓存文件读取表结构信息
				if (is_file($cachedir . $this->_table . '.php')){
					$properties = Helper::F($this->_table,false,$cachedir,'');
				}
			}
			if (empty($properties)){//从数据库中自动获取表结构信息
				$properties = $this->getDb()->getFields($this->_table);
				if (!empty($properties) && $saveTableCache)
					Helper::F($this->_table,$properties,$cachedir,'');//将表结构存储到缓存文件
			}
			$this->_properties = $properties;
		}elseif(is_array($field)){
			foreach ($field as $key=>$value){
				$this->_properties[$key] = array('value'=>$value,'updated'=>false);
			}
			unset($field);
		}else
			$this->_properties[$field] = array('value'=>$default,'updated'=>false);
	}
	
	/**
	 * 设置当前所操作的表名
	 */
	public function setTable($table){
		$this->_table = $table;
		$this->setField();
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name submit
	 * +------------------------------------------------------------+
	 * 提交表单操作，根据用户设置的，自动完成表单验证
	 * +------------------------------------------------------------+
	 *
	 * @example
	 *
	 * @param boolean $transaction 是否启用事务回滚功能
	 *
	 */
	public function submit($transaction = true){
		if (!$this->preSubmit()){
			if($transaction) $this->getDb()->rollback();
			return false;
		}
		if (!class_exists('Form')){
			require_once __CORE_PATH__ . 'Form.class.php';
		}
		
		$fp = new Form($this,$this->_rules);
		if (!$fp->submit($transaction)) {
			//记录验证中的错误信息
			$this->_errors = $fp->getError();
			return false;
		}else
			return true;
	}
	
	/**
	 * 更新数据；参数表示是否启用事务回滚功能
	 */
	protected function _update($transaction = true){
		if($transaction) $this->getDb()->begin();
		if (!$this->preUpdate()){
			if($transaction) $this->getDb()->rollback();
			return false;
		}
		
		if (!empty($this->_properties)) {
			foreach ($this->_properties as $k => $v){
				if ($v['updated']){
					$set[$k] = is_array($v['value']) ? serialize($v['value']) : $v['value'];
				}
			}
			if(!empty($set)){
				if(!$this->update($set,$this->_idField.'=?',$this->getId(),$this->_table)){
					if($transaction) $this->getDb()->rollback();
					return;
				}
			}
		}
		if(!$this->postUpdate()||!$this->postSubmit()){
			if($transaction) $this->getDb()->rollback();
			return;
		}
		
		if($transaction){
		    if ($this->getDb()->commit()){
		        $this->postSaved();
		        return true;
		    }else{
		        $this->getDb()->rollback();
		        return false;
		    }
		}else{
		    $this->postSaved();
		    return true;
		}
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name update
	 * +------------------------------------------------------------+
	 * 常用更新数据方法，该方法只支持单表更新操作
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param string $set 更新数据部分，形式为field1=value1,field2=value2或array(fidled1=>v1,field2=>v2)
	 * @param string $where 更新的条件语句，可包含占位符“?”
	 * @param mixed $replace 替换条件语句中的占位符“?”
	 * @param string $table 所操作的表
	 *
	 */
	public function update($set = false, $where=null , $replace=null, $table=null){
		if ($set === false){
			$sql = $this->_getSql(2);
			return $this->getDb('update')->exec($sql);
		}
		$table = empty($table) ? $this->_table : $table;
		
		//若传入的参数为数字并大于0，则更新ID为该值的数据行
		if (is_numeric($where) && $where > 0){
			$replace = $where;
			$where = $this->_idField . '=?';
		}
		//若未设置更新条件且更新数据包含更新主键的则将其设置为更新条件
		elseif ($where === null && isset($set[$this->_idField])){
			$where = $this->_idField . '=?';
			$replace = $set[$this->_idField];
			unset($set[$this->_idField]);
		}
		
		$set = $this->getSelect()->addQuoMark($set);
		$where = $this->getSelect()->quoteInto($where, $replace);
		
		return $this->getDb('update')->update($set, $where, $table);
	}
	
	/**
	 * 
	 * 方法update的别名
	 * 
	 * @param string $set
	 * @param string $where
	 * @param string $replace
	 * @param string $table
	 */
	public function edit($set = false, $where=null , $replace=null, $table=null){
		return $this->update($set, $where, $replace, $table);
	}
	
	/*****************************************************/
	/////Select查询语句设置方法 Start
	
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name column
	 * +------------------------------------------------------------+
	 * 设置所要查询字段
	 * +------------------------------------------------------------+
	 *
	 * @example
	 * $field的设置方法：1、'col1,col2,colN'   2、array('别名1'=>'col1', '别名N' => 'colN', 'colNN')
	 *
	 * @param mixed $field
	 *
	 */
	public function column($field=null){
		$this->getSelect()->column($field);
		
		return $this;
	}
	
	/*
	 * 设置查询结果唯一
	 */
	public function distinct(){
		$this->getSelect()->distinct(true);
		
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name from
	 * +------------------------------------------------------------+
	 * 设置查询表
	 * +------------------------------------------------------------+
	 *
	 *
	 * @example
	 * $table的设置方法： 1、table_name  2、array('别名1'=>'table_name1', '别名2'=>'table_name2', 'table_name3')
	 * $cols的设置方法：见方法column()
	 *
	 * @param mixed $table  设置表
	 * @param mixed $cols	设置查询字段，可为空
	 *
	 */
	public function from($table, $cols=null){
		$this->getSelect()->from($table, $cols);
		
		return $this;
	}
	
	/**
	 * from的别名
	 */
	public function table($table, $cols=null){
		return $this->from($table, $cols);
	}
	
	/**
	 * column的别名
	 */
	public function field($field){
		if (!empty($field)){
			$field = is_array($field) ? $field : explode(',', $field);
			foreach ($field as $k => $f){
				$f = trim($f);
				if ($this->hasField($f)){
					$f = '`' . $f . '`';
				}
				$field[$k] = $f;
			}
			$this->getSelect()->column($field);
		}
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name limit
	 * +------------------------------------------------------------+
	 * 设置LIMIT条件语句
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param int $count  每次取出条数
	 * @param int $offset 偏移量
	 *
	 */
	public function limit($count=null, $offset=null){
		$this->getSelect()->limit($count, $offset);
		
		return $this;
	}
	
	/**
	 * 数据库Like关键字查询
	 * 
	 * @param string $field
	 * @param string $value
	 * @param string $bool	true：AND关联查询  false：OR关联查询
	 * @return Model
	 */
	public function like($field, $value, $bool = true){
		$this->getSelect()->like($field, $value, $bool);
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name group
	 * +------------------------------------------------------------+
	 * 设置GROUP BY分组查询，支持多个，传入多个参数
	 * +------------------------------------------------------------+
	 *
	 *
	 * @example
	 * $Select->group('id')或$Select->group('id','name')
	 *
	 *
	 */
	public function group(){
		$spec = func_get_args();
		if (!empty($spec)){
			foreach ($spec as $val) {
				$this->getSelect()->group($val);
			}
		}
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name having
	 * +------------------------------------------------------------+
	 * 设置HAVING条件查询字句，有多个时默认按AND连接
	 * +------------------------------------------------------------+
	 *
	 * @example
	 *
	 * @param string $condition 条件，有传入值用占位符“?”表示
	 * @param mixed $replace 替换值，多个用数组
	 * @param boolean $bool 一个布尔值 ：true表示AND，flase表示OR
	 *
	 */
	public function having($condition, $replace=null, $bool=true){
		$this->getSelect()->having($condition, $replace, $bool);
		
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name page
	 * +------------------------------------------------------------+
	 * 设置分页查询
	 * +------------------------------------------------------------+
	 *
	 * @param int $p 当前页数
	 * @param int $count 每次显示条数
	 *
	 */
	public function page($p, $count = 20){
		$this->getSelect()->page($p, $count);
		
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name left
	 * +------------------------------------------------------------+
	 * 设置右连接
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param mixed  $tablename 表名（格式如：tablename或array(表别名=>tablename)）
	 * @param string $on 连接条件，单个字段会字段加上表前缀
	 * @param string $col 用法同类方法column的参数一致
	 *
	 */
	public function left($tablename, $on = '', $col=null){
	    if ($tablename !== false && $tablename){
	        if ($on !== ''){
	            $this->getSelect()->left($tablename, $on, $col);
	        }else{
	            die('on条件不能为空');
	        }
	    }
		
		return $this;
	}
	
	/**
	 * IN 查询
	 */
	public function in($field, $range = '', $bool = true){
		$this->getSelect()->in($field, $range, $bool);
		
		return $this;
	}
	
	/**
	 * NOT IN 查询
	 */
	public function notIn($field, $range = '', $bool = true){
		$this->getSelect()->notIn($field, $range, $bool);
		
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name inner
	 * +------------------------------------------------------------+
	 * 设置内联连接查询
	 * +------------------------------------------------------------+
	 *
	 * @author anzai <236049524@qq.com>
	 * @version 1.0
	 *
	 * @example
	 *
	 * @param mixed  $tablename 表名（格式如：tablename或array(表别名=>tablename)）
	 * @param string $on 连接条件，单个字段会字段加上表前缀
	 * @param string $col 用法同类方法column的参数一致
	 *
	 */
	public function inner($tablename, $on, $col=null){
		$this->getSelect()->inner($tablename, $on, $col);
		
		return $this;
	}
	
	/**
	 * 设置左连接，参数设置方法见left方法
	 */
	public function right($tablename, $on, $col=null){
		$this->getSelect()->right($tablename, $on, $col);
		
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name order
	 * +------------------------------------------------------------+
	 * 设置排序方式
	 * +------------------------------------------------------------+
	 *
	 *
	 * @param string $spec  查询字段
	 * @param boolean $direction 排序方式  true升序，false降序
	 *
	 */
	public function order($spec, $direction=false){
		if (is_array($spec)){
			$this->getSelect()->order($spec['spec'], $spec['direction']);
		}else{
			$this->getSelect()->order($spec, $direction);
		}
		
		return $this;
	}
	
	/**
	 * 随机排序
	 */
	public function rand(){
		$this->getSelect()->rand();
		
		return $this;
	}
	
	/**
	 * 设置自动升序排序
	 */
	public function asc($spec){
		if (is_array($spec)) $spec['direction'] = true;
		$this->getSelect()->order($spec, true);
		
		return $this;
	}
	
	/**
	 * 设置字段降序排序
	 */
	public function desc($spec){
		if (is_array($spec)) $spec['direction'] = false;
		$this->getSelect()->order($spec, false);
		
		return $this;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name where
	 * +------------------------------------------------------------+
	 * 设置where查询条件语句
	 * +------------------------------------------------------------+
	 *
	 * @param mixed  $condition 条件，有传入值用占位符“?”表示
	 * @param mixed  $replace 替换值，多个用数组，若为布尔值且$condition为数组则表示进行条件分组（true表示分组后用AND连接，flase表示OR）
	 * @param boolean $bool 一个布尔值 ：true表示AND，flase表示OR
	 * 如：
	 * $Select->where("id=$id") 或 $Select->where('id=?',$id) 或
	 * $Select->where(array('id'=>$id,...))
	 *
	 */
	public function where($condition,$replace=null,$bool=true){
		$this->getSelect()->where($condition, $replace, $bool);
		
		return $this;
	}
	
	/**
	 * 设置AND条件语句，参数设置方法见where方法
	 */
	public function andWhere($condition, $replace=null){
		$this->getSelect()->andWhere($condition, $replace);
		
		return $this;
	}
	
	/**
	 * 设置OR条件语句，参数设置方法见where方法
	 */
	public function orWhere($condition, $replace=null){
		$this->getSelect()->orWhere($condition, $replace);
		
		return $this;
	}
	
	/////Select查询语句设置方法 End
	/*****************************************************/
	
	
	/**
	 * 调用Model方法不存在时则默认调用Select中的方法
	 */
	public function __call($name,$args){
		if (method_exists($this->getSelect(), $name)){
			$count = count($args);
			$r = null;
			switch ($count){
				case $count>=4:
					$r = $this->getSelect()->$name($args[0], $args[1], $args[2], $args[3]);
					break;
				case 3:
					$r = $this->getSelect()->$name($args[0], $args[1], $args[2]);
					break;
				case 2:
					$r = $this->getSelect()->$name($args[0], $args[1]);
					break;
				case 1:
					$r = $this->getSelect()->$name($args[0]);
					break;
				default:
					$r = $this->getSelect()->$name();
			}
			return $r;
		}
		
		return $this;
	}
	
	/**
	 * PHP内置魔法函数，用于快速获取指定字段的值
	 */
	public function __get($name){
		if (isset($this->$name))
			return $this->$name;
		elseif(isset($this->_properties[$name]))
			return $this->_properties[$name]['value'];
		else 
			return parent::__get($name);
	}
	
	/**
	 * PHP内容魔法函数，用于快速设置指定字段值
	 */
	public function __set($name,$value){
		if(array_key_exists($name,$this->_properties)){
	        $this->set($name, $value);
    	}else{
    		$this->$name = $value;
    	}

    	return true;
	}
	
	/**
	 * 
	 * +------------------------------------------------------------+
	 * @name BuildMultiple
	 * +------------------------------------------------------------+
	 * 创建一组Model对象数组
	 * +------------------------------------------------------------+
	 *
	 * @param array $data 一个二维数组，其中必须包含ID字段
	 * @param string $idField
	 * @param string $table
	 * @param Model $class
	 *
	 */
	static public function BuildMultiple($data, $class=null, $table='', $idField=null){
		if (empty($data)) return;
		try{
			$class = empty($class) ? $GLOBALS['MODULE_NAME'] . MODEL_SUFFIX : $class;
			if (!class_exists($class))
				throw new E(Helper::L('CLS_MODEL_EXISTS'));
			$idField = $idField===null ? 'id' : $idField;
			foreach ($data as $row){
				$objs[$row[$idField]] = new $class($row[$idField], $table, $idField);
			}
		}catch (E $e){
			$e->getMsg();
		}
		
		return $objs;
	}
	
}

?>